Uso da Porta Paralela do PC para Controle e Aquisição de Dados

Uso da Porta Paralela do PC para Controle e Aquisição de Dados


Lucas
http://www.litoralsp.com.br/lucas
lenalex@uol.com.br


Porta Paralela - Introdução
Saídas
Entradas
Pequeno Exemplo
Circuito de Teste
Pinos Bidirecionais - 8 Entradas/8 Saídas
Detectando as portas LPT

A. Porta Paralela - Introdução

A porta paralela consiste básicamente de três partes: porta de dados, status e controle. Estas portas e seus respectivos endereços estão em ordem sequencial. Então, se o endereço da porta de dados é 0x378, então sua porta correspondente de status é 0x379 e de controle é 0x37a.

 
        Porta         Porta Dados       Status         Controle      
        LPT1            0x0378          0x0379          0x037a      
        LPT2            0x0278          0x0279          0x027a 

Para identificar o endereço das portas de um computador, use o debug do DOS para mostrar a localização de memória 0040:0008. Por exemplo:

    
	>debug      
	-d 0040:0008 L8      
	0040:0008       78 03 78 02 00 00 00 00    

Note neste exemplo que LPT1 é 0x0378, LPT2 é 0x0278 e LPT3 e LPT4 não estão disponíveis.

Um outro modo é rodar o Microsoft Diagnostic (msd.exe) e verificar as configurações da LPT.


B. Saídas

Veja as figuras entituladas Figura #1 - Pinagem e Figura #2 - Configuração das Portas. Estas duas figuras ilustram a configuração dos pinos no conector de 25 pinos e as configurações dos bits nas três portas (dados, status e controle).


Fig 1. Pinagem


Fig 2. Configuração das Portas


Observe que estas portas possuem 8 saídas na porta data (Data 7(msb) - Data 0) e quatro saídas adicionais no nibble inferior da porta controle (/SELECT_IN, INIT, /AUTO FEED and /STROBE).

Todas as saídas da porta dados são de lógica direta. Isto é, colocando um bit desta porta em 1, sua saída correspondente vai ao nível alto. Entretanto, as saídas /SELECT_IN, /AUTOFEED e /STROBE da porta controle possuem lógica invertida. Isto é, colocando um destes em 1, sua saída correspondente vai ao nível baixo. Isto pode parecer complicado, mas basta inverter estes bits através de software usando uma intrução XOR.

Imagine que você tenha um valor val1 que será enviado para a porta dados e um valor val2 na porta controle:

    
	#define DADOS 0x0378      
	#define STATUS DADOS+1      
	#define CONTROLE DADOS+2      
	...      
	int val1, val2;      
	...      
	val1 = 0x81;    /* 1000 0001 */
	   outportb(DADOS, val1);      /* Envia val1 para porta dados */
	val2 = 0x08;    /* 0000 1000 */      
	   outportb(CONTROLE, VAL2^0x0b); /* Envia val2 para porta controle */     
	/* 0x0b= 0000 1011 SELECT_IN = 1, INIT = 0, /AUTO_FEED = 0, /STROBE = 0 */    

Observe que apenas os 4 bits menos significativos de val2 são utilizados. Na última linha de código, /SELECT_IN, /AUTO_FEED e /STROBE são enviados de forma invertida usando a função OU-EXCLUSIVO (XOR) para compensar a inversão de hardware.

Por exemplo: se você quiser enviar 1 0 0 0 no nibble baixo e não fizer a inversão, o harware irá inverter o bit 3, não inverterá o bit 2 e inverterá os bits 1 e 0. O resultado na saída será então 0 0 1 1 que está longe do desejado na saída. Usando uma função ou-exclusivo, 1 0 0 0 é enviado para a porta controle como 0 0 1 1. O hardware então inverte os bits 3, 1 e 0 e na saída teremos 1 0 0 0.


C. Entradas

A porta status possui 5 sinais: BSY, /ACK, PE (paper empty), SELECT, /ERROR.

Estas entradas são lidas através dos 5 bits mais significativos da porta de status.

Entretando, os designers originais dos circuitos de interface da impressora, inverteram o bit associado a BSY usando hardware. Isto é, quando um zero está presente na entrada BSY, o bit será lido na porta status como '0'. Então, para ler o bit basta invertê-lo via software.

O seguinte trecho demonstra a leitura dos 5 bits mais significativos.

    
	#define DADOS 0x0378      
	#define STATUS DADOS+1      
	...      
	unsigned int in_val;      
	...      
	in_val = ((inportb(STATUS)^0x80) >> 3);
	/* lê a porta status e faz XOR com 1000 0000 */    

Observe que a porta status é lida e o bit mais significativo, correspondente a BSY, é invertido utilizando a função ou-exclusivo (XOR). O resultado então é deslocado para que os 5 bits superiores fiquem nos 5 bits inferiores da variável in_val.

A entrada IRQ na porta status não está disponível nos terminais do conector DB-25.

Até aqui, vimos que temos disponívies até 12 saídas; oito na porta dados e quatro na porta controle. Temos também 5 entradas na porta status. Três bits da porta controle e o bit mais significativo da porta status são invertidas pelo hardware, mas isto é facilmente manipulado pela função XOR para selecionar os bits invertidos.


D. Pequeno Exemplo

Observe a Figura #3 - Interruptor Digital Simples mostrando um push-buttom normalmete aberto que é lida na entrada BSY (porta status, bit 7) e o led que é acionado pelo bit 0 na porta dados. Um programa em linguagem C faz com que quando o interruptor seja pressionado, aterrando o pino BUSY, o led pisque. Note que o led acenderá com nível 0 na saída.

     
	/* LED.C by Lucas*/    
	#include <stdio.h>     
	#include <dos.h>   /* função delay */     
	     
	#define DADOS 0x0378     
	#define STATUS DADOS+1     
	#define CONTROLE DADOS+2     
	     
	void main(void)     
	{     
	   int in;     
	   while(1)     
	   {     
	      in = inportb(STATUS);     
	      if (((in^0x80)&0x80)==0)     
	      /* se o bit BUSY estiver em 0 (chave fechada) */     
	      {     
	         outportb(DADOS,0x00);   /* liga o LED */     
	         delay(100);     
	         outportb(DADOS, 0x01);   /* apaga o LED */     
	         delay(100);     
	      }     
	      else     
	      {     
	         outportb(DADOS,0x01);     
	         /* se a chave está aberta, apaga o LED */     
	      }     
	   }     
	}     

Fig 3 - Interruptor Digital Simples

Descrição do circuito: Nível 1 na saída D0 (porta dados - Bit 0) apaga o LED. Nível 0 causa o acendimento do LED.

Quando a chave está aberta, temos 5V (nível 1) na entrada BUSY via resistor BUSY (porta status - Bit 7). Quando pressionado, o push-buttom aterra (nível 0) o resistor e o pino BUSY.

Observe que utilizamos uma fonte externa para o acinamento do LED.


E. Circuito de teste

Observe a Figura #4 - Circuito de Teste da Porta Paralela. Este esquema demonstra uma forma simples de testar a porta paralela e o programa demonstra as inversões necessárias para o correto controle da porta paralela. O programa teste.c acende todos os LEDS e vai apagando um por um. Depois, fica em um loop monitorando as 5 entradas e mostrando o resultado na tela.


Fig 4. Circuito de Teste da Porta Paralela


   
	/* TESTE.C   
	**   
	** Programa para teste das 12 saídas e 5 entradas da porta paralela
	**   
	** O programa apaga sequencialmente os LEDs nos Bits 7, 6, 5, ... 0 da   
	** porta dados, e depois os bits 3, 2, 1 and 0 da porta controle. Cada 
	** LED fica apagado por cerca de 1 segundo. Observe que o led é apagado   
	** com nível 1. Este processo é executado uma vez.
	**   
	** Então o programa entra em um loop, scaneando os 5 bits mais significativos
	** da porta status e mostra continuamente o seu conteúdo em hexadecimal.   
	**   
	** Lucas
	*/   
   
	#include <stdio.h>   
	#include <dos.h>        /* utilizado para função delay */   
	   
	#define DADOS 0x0378     /* endereço da porta dados */   
	#define STATUS DADOS+1   
	#define CONTROLE DADOS+2   
   
	void main(void)   
	{   
	   int in, n;   
   
	   outportb(DADOS,0x00); /* acende todos os LEDs da porta dados */   
	   outportb(CONTROL, 0x00^0x0b); /* acende todos da porta controle */   
	      
	   /* apaga cada LED da porta dado colocando nível 1 na respectiva saída */   
	   for (n=7; n>=0; n++)   
	   {   
	      outportb(DADOS, 0x01 << n);   
	      delay(1000);   
	   }   
	   outportb(DADOS, 0x00);   
   
	   /* agora, apaga cada LED da porta controle   
	   ** observe a função XOR para compensar a inversão do hardware
	   */   
   
	   outportb(CONTROLE, 0x08^0x0b);        /* bit 3 */   
	   delay(1000);   
	   outportb(CONTROLE, 0x04^0x0b);        /* bit 2 */   
	   delay(1000);   
	   outportb(CONTROLE, 0x02^0x0b);        /* bit 1 */   
	   delay(1000);   
	   outportb(CONTROLE, 0x01^0x0b);        /* bit 0 */   
	   delay(1000);   
   
	   outportb(CONTROLE, 0x00);   
   
	   /* scaneia continuamente a porta status e mostra seu conteúdo em hexadecimal */   
   
	   while(1)   
	   {   
	      in = (inportb(STATUS)^0x80)&0xf8;   
	      /* Oberse que o bit BUSY é invertido através do XOR */
	      printf("%x\n", in);   
	   }   
	}   
   

F. Pinos Bidirecionais - 8 Entradas/8 Saídas

No endereço base (porta dados), oito bits estão disponíveis para saída nos pinos 2-9.

No endereço base+1 (porta status), temos 5 entradas nos bits D3 a D7. O bit D6 (pino 10) pode ser usado para gerar uma interrupção de hardware.

No endereço base+2 (porta controle) temos diversas possibilidades. Temos 4 bits de saída ou 4 bits de entrada ou podem ser configurados da maneira que se deseja. Isto é possível porque as saídas estão em coletor aberto. Enviando dados para esta porta faz com que os pinos de saída vá para o nível alto, e assim este pino pode ser utilizado como uma entrada. O coletor aberto é polarizado através de um resistor de 4,7K.

Resumindo: a porta paralela é capaz de fornecer de oito até 12 saídas e de cinco a até nove pinos de entrada. Um destes pinos pode também ser usado como interrupção de hardware, colocando D4 (porta controle) em 1 e aplicando um pulso no pino 10. O IRQ padrão da LPT é 7.

O circuito abaixo é exemplo de um circuito de 8 entradas/8 saídas.


Abaixo, a sequência de instruções para ler e transformar as 8 entradas em 1 byte. O X indica bits de usaremos enquanto ? são desconhecidos e serão colocados em zero. Azul são bits normais e vermelho os bits que necessitam ser invertidos. Começamos colocando os bits desconhecidos em zero
val = inportb(STATUS) & 0xF0

inportb(STATUS)
X X X X ? ? ? ?
& 0xF0
1 1 1 1 0 0 0 0
Igual a
val1
X X X X 0 0 0 0


O mesmo procedimento será usado para as entradas da porta controle, lembrando que os 4 bits superiores são zerados e os 4 bits inferiores são dados.

val2 = inportb(CONTROLE) & 0x0F

inportb(CONTROLE)
? ? ? ? Y Y Y Y
& 0x0F
0 0 0 0 1 1 1 1
Igual a
val2
0 0 0 0 Y Y Y Y


Agora temos 2 bytes que precisam ser combinados. Para combiná-los utilizaremos a intrução OR.

valor = val1 | val2;

val1
X X X X 0 0 0 0
| val2
0 0 0 0 Y Y Y Y
igual a
valor
X X X X Y Y Y Y


Os bits D0, D1, D3 e D7 precisam ser invertidos. A instrução XOR será usada para mudar estes 4 bits: use 1 para inverter e zero para não inverter.

valor = valor ^ 0x8B

valor
X X X X Y Y Y Y
XOR 0x8B
1 0 0 0 1 0 1 1
forma correta
valor
X X X X Y Y Y Y


G. Detectando as portas LPT

CLS

' Programa em QBASIC
' A BIOS coloca os endereços das portas LPT começando pelo endereço 0x408
' O byte menos significativo da LPT1 fica em 0x408 e o mais significativo em 0x409
' O byte menos significativo da LPT2 fica em 0x40A e o mais significativo em 0x40B
' O byte menos significativo da LPT1 fica em 0x40C e o mais significativo em 0x40D
' Os possíveis valores encontrados podem ser: 0x278, 0x378, 0x3BC ou 0 se não estiver instalada

' Procura se as portas LPT estão instaladas
DEF SEG = 0
lpt1 = PEEK(&H408) + 256 * PEEK(&H409)
lpt2 = PEEK(&H40A) + 256 * PEEK(&H40B)
lpt3 = PEEK(&H40C) + 256 * PEEK(&H40D)
DEF SEG

IF lpt1 = 0 THEN
        PRINT "1.  Lpt1 não está instalada."
ELSE
        PRINT "1.  Lpt1 está instalada no endereço "; lpt1; " decimal."
END IF

IF lpt2 = 0 THEN
        PRINT "2.  Lpt2 não está instalada."
ELSE
        PRINT "2.  Lpt2 está instalada no endereço "; lpt2; " decimal."
END IF

IF lpt3 = 0 THEN
        PRINT "3.  Lpt3 não está instalada."
ELSE
        PRINT "3.  Lpt3 está instalada no endereço "; lpt3; " decimal."
END IF

'  Entrada da porta a utilizar
PRINT

DO
    INPUT "Qual porta LPT será utilizada: 1, 2, or 3?  ", N%

' Associa Base0% ao endereço da porta LPT escolhida 

    IF N% = 1 THEN Base0% = lpt1
    IF N% = 2 THEN Base0% = lpt2
    IF N% = 3 THEN Base0% = lpt3

    IF Base0% = 0 THEN
        PRINT "Está porta não existe, escolha novamente!"
        N% = 0
    END IF

LOOP WHILE N% > 3 OR N% < 1

Base1% = Base0% + 1  'associa a porta LPT com sua porta de Status
Base2% = Base0% + 2  'associa a porta LPT com sua porta de Controle

'Aqui entra o programa utilizando as variáveis Base0%, Base1% e Base%2
'para as respectivas portas de entrada e saída.

END

Durante a inicialização, a BIOS verifica pelas três portas LPT nos endereços de IO 0x378, 0x278 e 0x3BC. Ele coloca estes endereços na memória iniciando pelo endereço 0000:0408 hexa com o byte menos significativo vindo na frente, seguido pelo byte mais significativo. Se uma das portas não estiver instalada é colocado um zero na sua localização de memória.

A tabela abaixo mostra como se pareceria a memória do seu computador se houvesse duas portas LPT instaladas nos endereços 0x378 e 0x278.

Manipulador do DOS Endereço LSB MSB Decimal
LPT1 0000:0408/9 78 03 888
LPT2 0000:040A/B 78 02 632
LPT3 (não instalada) 0000:040C/D 00 00 0

Esta programa pega os valores no endereços de memória correspondentes e associa aos manipuladores do DOS. Em seguida ele pergunta qual das portas instaladas vai ser utilizada. Depois associa os endereços das portas de status e controle a porta LPT selecionada. Este programa pode também ser implementado em diversas outras linguagens.